home *** CD-ROM | disk | FTP | other *** search
- /* Copyright 1984 by the Massachusetts Institute of Technology */
-
- /*
- Copyright Cornell University 1986. All rights are reserved.
- As of 4/10/86:
- This source file may have no changes from the M.I.T original
- other than this notice; but it has been tested as part of
- Cornell's Aztec-C port. See notice.h
- */
-
- /* See permission and disclaimer notice in file "notice.h" */
-
- /* Addition of tm_tset and tm_mset, 12/83. <J.H. Saltzer> */
- /* 8/19/86 kevin increased TIMERSTACK to 4000 so Alert() in timer
- upcall won't cause crash */
- /* 11/19/86 kevin decreased stack to 2000 */
- /* 8/21/87 kevin decreased stack to 1000 */
- /* 10/16/87 kevin changed q_adda to q_addb so timers really are
- in timeout order */
- /* 10/16/87 kevin made timer q traversals more intelligible */
- /* 3/16/88 kevin removed register declarations which caused bugs */
- /* 3/16/88 kevin made sure tnonce never == 0 */
- /* 7/24/89 kevin rewrote for MacTCP, added alarm.c */
-
- #include <notice.h>
-
- /* This file contains the routines which make up the timer task and
- * its associated subroutines for setting and clearing timers. The
- * timer task manages a queue of timers, each of which specifies a
- * subroutine to be called (in the context of the timer task) when the
- * timer goes off.
- * The following routines are included in this package:
- * tm_set set a timer to fire after number of seconds
- * tm_mset set a timer, argument in milliseconds
- * tm_tset set a timer, argument in clock ticks
- * tm_reset reset a timer to go off at a different time
- * tm_clear clear a previously set timer
- * tm_main the main routine of the timer task
- * tm_init init the timer system
- * tm_off turn off timer interrupts
- * tm_on turn timer interrupts back on
- */
-
- #include <q.h>
- #include <timer.h>
- #include <retrace.h>
- #include <memory.h>
-
- #include <emdefs.h>
-
- extern char *malloc();
-
- #define TIMERHIWATER 30 /* number of free timers to keep */
-
- time_q tm_queue; /* queue of active timers */
- time_q tm_freeq; /* queue of free timers */
- nonce tnonce; /* timer nonce generator */
- char timertripped = FALSE; /* the timer requires service */
-
- unsigned long cticks = 0L;
- unsigned long next_alarm = 0L;
- unsigned long oldalarm;
- unsigned TIMERDEBUG = 0;
-
- /* set timer in seconds */
-
- tm_set(nsecs, subr, arg, tm)
- int nsecs; /* timer expiration time, in secs */
- int (*subr)(); /* subroutine to call on expiration */
- char *arg; /* arg to pass to subr. */
- timer *tm; /* place to return timer id */
- {
- tm_tset(nsecs*TPS, subr, arg, tm);
- }
-
-
- /* set timer in milliseconds */
-
- tm_mset(msecs, subr, arg, tm)
- long msecs; /* timer expiration time, in msecs */
- int (*subr)(); /* subroutine to call on expiration */
- char *arg; /* arg to pass to subr. */
- timer *tm; /* place to return timer id */
- {
- tm_tset((int)((msecs*TPS)/1000), subr, arg, tm);
- }
-
-
- /* Set a timer to go off after nticks clock ticks. When the timer
- goes off, call the specified subroutine with the specified
- argument flag. This routine enqueues the timer and,
- if it is the first timer
- on the queue, sets an flag to call the timer task.
- */
-
- tm_tset(nticks, subr, arg, tm)
- int nticks; /* timer expiration time */
- int (*subr)(); /* subroutine to call on expiration */
- char *arg; /* arg to pass to subr. */
- timer *tm; /* place to return timer id */
- {
- timer *tmp; /* temp for chaining */
- queue *qp;
-
- #ifdef DEBUG
- if (TIMERDEBUG) {
- printf("TM_SET: %8X set for %u ticks\n", tm, nticks);
- }
- #endif
-
- if (tm == (timer *) NULL)
- return(-1);
-
- q_del(&tm_queue, tm); /* make sure not already queued. */
- tm->tm_elt.qe_next = NULL; /* no next element */
- tm->tm_time = cticks + nticks; /* timer expiration time */
- /* TODO should check for wrap */
- if (!++tnonce)
- /* make sure it skips zero */
- ++tnonce;
- tm->tm_nonce = tnonce; /* nonce for this timer */
- tm->tm_subr = subr; /* subroutine to call */
- tm->tm_arg = arg; /* argument to pass */
-
- /* add to queue in timeout order */
- for (tmp = tm_queue.tmq_head;
- tmp != NULL && tmp->tm_time <= tm->tm_time;
- tmp = (timer *) tmp->tm_elt.qe_next)
- ;
-
- qp = (queue *) &tm_queue;
- q_addb(qp, (q_elt) tmp, (q_elt) tm);
- if (tm_queue.tmq_head == tm) /* first elt in queue? */
- set_alarm(nticks); /* yes, wake up timer task then */
-
- }
-
-
- /* Reset a (running) timer to go off in nsecs seconds
- instead of at the time it is currently set for. If in fact the
- timer is not already set, return FALSE; otherwise return TRUE.
- Does not modify the upcall in the timer.
- */
-
- tm_reset(nsecs, tm)
- int nsecs; /* timer expiration time in seconds */
- timer *tm; /* timer id to reset*/
- {
-
- timer *tmp; /* temp for chaining */
- queue *qp;
-
- if (tm->tm_nonce == 0 || ! q_del(&tm_queue, tm)) {
- #ifdef DEBUG
- if (TIMERDEBUG)
- printf("TM_RST: %8X already expired.\n", tm);
- #endif
- return(FALSE); /* timer expired, give up */
- }
-
- #ifdef DEBUG
- if (TIMERDEBUG)
- printf("TM_RST: %8X reset for %u seconds.\n", tm, nsecs);
- #endif
-
-
- tm->tm_elt.qe_next = NULL; /* no next element */
- tm->tm_time = cticks + nsecs * TPS; /* timer expiration time */
-
- for (tmp = tm_queue.tmq_head;
- tmp != NULL && tmp->tm_time <= tm->tm_time;
- tmp = (timer *) tmp->tm_elt.qe_next)
- /* add to queue in timeout order */
- ;
-
- qp = (queue *)&tm_queue;
- q_addb(qp, (q_elt)tmp, (q_elt)tm);
-
- if (tm_queue.tmq_head == tm) /* first elt in queue? */
- set_alarm(nsecs*TPS); /* yes, wake up timer task then */
-
- return(TRUE);
- }
-
-
-
- /* Clear the timer specified by the passed timer identifier. The
- timer identifier gives a pointer to the timer to be cleared.
- Free the timer's storage
- (into the free list up to TIMERHIWATER elements).
- If it was the only timer on the queue, reset the pending alarm.
- Returns FALSE if the specified alarm was not found in the queue
- TRUE otherwise.
- */
-
- tm_clear(tm)
- timer *tm; /* identifier of timer to clear */
- {
-
- if (tm->tm_nonce == 0) {
- #ifdef DEBUG
- if (TIMERDEBUG)
- printf("TM_CLR: %8X already expired.\n", tm);
- #endif
- return FALSE;
- }
-
- #ifdef DEBUG
- if (TIMERDEBUG)
- printf("TM_CLR: %8X\n", tm);
- #endif
-
- tm->tm_nonce = 0;
- if(!q_del(&tm_queue, tm))
- return(FALSE); /* timer expired, give up */
-
- if (tm_queue.tmq_head == NULL) /* last elt on queue? */
- set_alarm(-1); /* yes, turn off alarm */
-
- return(TRUE); /* success */
-
- }
-
-
- /* This routine forms the body of the timer management task. Its
- job is simply to dequeue expired timers from the timer queue
- and call the subroutine specified therein, passing it the argument
- specified therein.
- Note that this task needs the alarm signal, so all timer
- management in the process must be via this task - no one else
- may use the alarm() calls!
- */
-
- tm_main()
- {
- timer *tm_tmp; /* temp for holding timer */
-
- timertripped = FALSE;
- while ((tm_tmp = tm_queue.tmq_head) != NULL
- && cticks >= tm_tmp->tm_time) {
- /* for all expired timers */
-
- tm_tmp = (timer *) q_deq(&tm_queue); /* dequeue it */
-
- /* if the timer is expired, ignore it */
- if (tm_tmp->tm_nonce == 0) {
- #ifdef DEBUG
- if (TIMERDEBUG)
- printf(" TIMER: %8X already fired.\n", tm_tmp);
- #endif
- continue;
- }
-
- #ifdef DEBUG
- if (TIMERDEBUG)
- printf(" TIMER: %8X firing.\n", tm_tmp);
- #endif
-
- tm_tmp->tm_nonce = 0; /* show timer expired... */
- (*tm_tmp->tm_subr)(tm_tmp->tm_arg); /* call its routine */
-
- }
-
- if (tm_queue.tmq_head != NULL) {
- /* there are outstanding timers, reset the alarm */
- set_alarm((int)(tm_queue.tmq_head->tm_time - cticks));
- }
- }
-
-
- /* Initialize the timer package. Set up the alarm routine tm_signal(). */
-
- tm_init()
- {
- extern int alarm_clnup();
-
- alarm_init(); /* set up out alarm function */
- exit_hook(alarm_clnup);
- }
-
-
-
- /* Turn off timer interrupts. This is useful while writing data to
- the terminal. */
-
- tm_off()
- {
- oldalarm = next_alarm;
- set_alarm(-1);
- }
-
-
- /* Turn timer interrupts back on. This is done by running the
- timer task to process any events that went off while timer
- interrupts were disabled. If anything else is queued, that task
- will set a new alarm. */
-
- tm_on()
- {
- tm_main();
- }
-
- /*****/
-
- timer *tm_alloc()
- {
-
- /* Allocate a timer and return a pointer to it */
-
- timer *t;
- if((t = (timer *)q_deq(&tm_freeq)) == NULL &&
- (t = (timer *)malloc(sizeof(timer))) == NULL) return NULL;
-
- t->tm_elt.qe_next = NULL;
- return t;
- }
-
- /*****/
-
- tm_free(t)
- timer *t;
- {
- /* Free up a timer. Returns true if successful, false otherwise */
-
- timer **tmp;
- queue *qp;
-
- /* Check if the timer is enqueued */
-
- for(tmp = &tm_queue.tmq_head; *tmp != NULL;
- tmp = (timer **)(((timer *)tmp)->tm_elt.qe_next))
- if(*tmp == t) {
- #ifdef DEBUG
- printf("Tried to free active timer.\n");
- #endif
- return FALSE;
- }
-
- if (tm_freeq.tmq_len < TIMERHIWATER)
- {
- qp = (queue *)&tm_freeq;
- q_addh(qp, (q_elt)t);
- }
- /*
- else
- cfree(t);
- */
-
- return TRUE;
- }
-
- /*****/
-
- tm_qdump()
- {
- /*
- Dump some information about the timer queue to the display. Used
- for debugging. Takes a timer * as an argument and tells if it's
- in the queue.
- */
-
- printf("Nonce = %u\n", tnonce);
- printf("timer queue:\n");
- printf("\thead %8X\ttail %8X\n", tm_queue.tmq_head,
- tm_queue.tmq_tail);
- printf("\tlength %u\n", tm_queue.tmq_len);
- }
-
- /*****/
-
- tm_dump(t)
- timer *t;
- {
- /* For debugging, dump the interesting parts of a timer. */
-
- printf("dump of timer %8X:\n", t);
- printf(" fire time is %8X, nonce is %u, next q element is %8X\n",
- t->tm_time, t->tm_nonce, t->tm_elt.qe_next);
-
- }
-
-
- /* from task/alarm.c */
-
-
- /*
- Copyright 1984 M.I.T.
- Copyright Cornell University 1986. All rights are reserved.
-
- As of 4/10/86:
- This source file may have no changes from the M.I.T original
- other than this notice; but it has been tested as part of
- Cornell's Aztec-C port. See notice.h
-
- */
-
- /* 5/8/87 kevin modified to remove saveA5() and avoid a5 problems john discovered */
- /* 4/13/88 kevin modified for MultiFinder: allocated VBL task in system heap */
-
- #define vType 1
-
- struct a5save {
- char * mya5; /* saves the a5 */
- VBLTask mytask;
- } myvbl;
-
- alarm_init()
- {
- void signal();
- short status;
- THz appzone;
-
- appzone = GetZone();
- SetZone(SystemZone());
- /* allocate the alarm in the system heap
- so that it gets service even when MultiFinder
- switches out application */
-
- myvbl.mytask.qType = vType;
- myvbl.mytask.vblAddr = signal;
- myvbl.mytask.vblCount = 1; /* call every tick */
- myvbl.mytask.vblPhase = 0;
-
- myvbl.mya5 = (char *) (* (long *) 0x904);
- status = VInstall(&myvbl.mytask);
- SetZone(appzone);
- if (status) {
- error("Can't install retrace routine");
- return(-1);
- }
- return(0);
- }
-
- alarm_clnup()
- {
- short status;
- THz appzone;
-
- appzone = GetZone();
- SetZone(SystemZone());
-
- status = VRemove(&myvbl.mytask);
- SetZone(appzone);
- }
-
-
- /* this routine is called by the VBL timer every tick */
-
- void signal()
- {
- #asm
- move.l a5,-(a7)
- movea.l -4(a0),a5
- ; move the a5 we saved earlier into a5
- move.w #1,10(a0)
- ; task->vblCount = 1; doesn't work on a Mac Portable!
- #endasm
-
- cticks++;
- if (!cticks) {
- /* we've wrapped around, reset times on q that have popped */
- tm_wrap();
- user_tmwrap();
- }
- if (next_alarm)
- /* don't decrement if already zero */
- if (--next_alarm == 0L)
- timertripped = TRUE;
-
- ; /* null statement so asm directive gets spotted! */
- #asm
- move.l (a7)+,a5
- #endasm
- }
-
-
- set_alarm(ntime)
- int ntime;
- {
- if (ntime >= -1) {
- /* ignore weird args */
- next_alarm = ntime + 1;
- }
- }
-
-
-
- tm_wrap()
- {
- timer *timerp;
-
- for (timerp = tm_queue.tmq_head; timerp != NULL;
- timerp = (timer *) timerp->tm_elt.qe_next) {
-
- /* if the time value is large, reset it so the timer will service it */
- if (timerp->tm_nonce) {
- if (timerp->tm_time > 3000000)
- timerp->tm_time = 0;
- }
- }
- }
-
-
-